#include "pg_school.h"
#define SCORE_FIELDS_COUNT 5

static bool check_student_exists_by_id(int16 student_id)
{
    Relation student_rel = NULL;
    SysScanDesc scanDesc = NULL;
    ScanKeyData scanKey;
    HeapTuple tuple = NULL;
    bool exists = false;

    // 打开 student 表
    Oid student_relid = get_relname_relid(TABLESTUDENT, get_namespace_oid(SCHEMANAME, false));
    if (!OidIsValid(student_relid))
    {
        elog(ERROR, "Table %s does not exist in schema %s", TABLESTUDENT, SCHEMANAME);
        return false;
    }

    student_rel = table_open(student_relid, AccessShareLock);
    if (!RelationIsValid(student_rel))
    {
        elog(ERROR, "Failed to open table %s", TABLESTUDENT);
        return false;
    }

    // 创建 ScanKey，用于按 id 扫描
    ScanKeyInit(&scanKey,
                Anum_changjunhighschool_student_id,  // student 表中的 id 列号
                BTEqualStrategyNumber,
                F_INT2EQ,
                Int16GetDatum(student_id));

    // 开始扫描 student 表
    scanDesc = systable_beginscan(student_rel, InvalidOid, false, NULL, 1, &scanKey);

    // 检查是否有匹配的记录
    if ((tuple = systable_getnext(scanDesc)) != NULL)
    {
        exists = true;
    }

    // 结束扫描
    systable_endscan(scanDesc);
    table_close(student_rel, AccessShareLock);

    return exists;
}

static int insert_scores(Relation rel, ArrayType *scores_array)
{
    Datum *values = NULL;
    bool *nulls = NULL;
    int n_scores = 0;
    int rows = 0;
    // 检查 scores_array 是否为 NULL
    if (scores_array == NULL)
    {
        elog(WARNING, "Input array is NULL");
        return 0;
    }
    // 解构数组，将其转换为 Datum 数组和 nulls 数组
    deconstruct_array(scores_array, TEXTOID, -1, false, 'i', &values, &nulls, &n_scores);

    // 检查数组是否为空
    if (n_scores == 0)
    {
        elog(WARNING, "Input array is empty, no records to insert");
        return 0;
    }

    // 遍历每个成绩记录
    for (int i = 0; i < n_scores; i++)
    {
        if (!nulls[i])  // 检查元素是否为 NULL
        {
            char *score_record = text_to_cstring(DatumGetTextP(values[i]));
            int32 chinese_score = 0;
            int32 math_score = 0;
            char evaluate[8] = {0};
            int16 id = 0;
            int64 ranking = -1;
            char *id_buffer = NULL;
            char *ranking_buffer = NULL;
            Datum score_values[5] = {0};
            bool score_nulls[5] = {false,false,false,false,false};
            HeapTuple tuple = NULL;
            // 解析 score_record
            sscanf(score_record, "%d,%d,%7[^,],%hd,%ld", \
                    &chinese_score, &math_score, evaluate, &id, &ranking);

            // 检查 student 表中是否存在相同 id 的记录
            if (!check_student_exists_by_id(id))
            {
                elog(WARNING, "No student found with id: %hd, skipping insertion", id);
                continue;
            }

            //检查id列和ranking列的值是否已经存在
            id_buffer = psprintf("%hd",id);
            ranking_buffer = psprintf("%ld",ranking);
            if (check_value_exists(rel,"id",id_buffer,NULL,NULL,TABLESCORE) || 
                            check_value_exists(rel,"ranking",ranking_buffer,NULL,NULL,TABLESCORE))
            {
                continue;
            }
            // 创建 HeapTuple
            score_values[0] = Int32GetDatum(chinese_score); // chinese_score
            score_values[1] = Int32GetDatum(math_score);    // math_score
            score_values[2] = CStringGetTextDatum(evaluate); // english_score
            score_values[3] = Int16GetDatum(id);            // id
            score_values[4] = Int64GetDatum(ranking);       // ranking
            
            // 创建新的 HeapTuple
            tuple = heap_form_tuple(RelationGetDescr(rel), score_values, score_nulls);

            // 批量插入
            CatalogTupleInsert(rel,tuple);
            CommandCounterIncrement();
            // 释放 HeapTuple
            pfree(tuple);
            pfree(id_buffer);
            pfree(ranking_buffer);
            rows++;
        }
    }
    log_operateRows(TABLESCORE,insertType,rows);
    return rows;
}

PG_FUNCTION_INFO_V1(insert_score_nospi);

Datum insert_score_nospi(PG_FUNCTION_ARGS)
{
    ArrayType *scores_array = NULL;
    Relation rel = NULL;
    int rows = 0;
    // 获取关系的 OID
    Oid relOid = get_relname_relid(TABLESCORE, get_namespace_oid(SCHEMANAME, false));
    
    if (!OidIsValid(relOid))
        elog(ERROR, "Relation does not exist");

    // 获取输入的数组
    scores_array = PG_GETARG_ARRAYTYPE_P(0);
    
    // 打开表
    rel = table_open(relOid, RowExclusiveLock);

    // 调用插入成绩函数
    rows = insert_scores(rel, scores_array);

    // 关闭表
    table_close(rel, RowExclusiveLock);

    PG_RETURN_INT32(rows);
}




PG_FUNCTION_INFO_V1(delete_scores_nospi);

Datum delete_scores_nospi(PG_FUNCTION_ARGS) {
    Relation rel = NULL;
    HeapTuple tuple = NULL;
    SysScanDesc scanDesc = NULL;
    int32 rows = 0; // 操作行数
    StrategyNumber strategy = 0;
    RegProcedure procedure = 0;
    char *text_value = NULL;
    ScanKeyData scanKey;
    Datum scan_argument;
    Oid schemaId = -1;
    Oid relid = -1;
    AttrNumber col_index = 0;
    int col_type = 0;
    // 获取参数
    char *col_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
    char *cmp_operator = text_to_cstring(PG_GETARG_TEXT_PP(1));
    text_value = text_to_cstring(PG_GETARG_TEXT_PP(2));

    schemaId = get_namespace_oid(SCHEMANAME, false);
    relid = get_relname_relid(TABLESCORE, schemaId);

    // 确定列索引
    col_index = get_column_index(TABLESCORE,col_name);
    col_type = get_column_type(col_name);

    // 检查 col_name 是否为空
    if (col_name == NULL || strlen(col_name) == 0) {
        elog(WARNING, "Column name cannot be NULL or empty");
        PG_RETURN_INT32(0);
    }

    // 检查 cmp_operator 是否为有效的运算符
    if (strcmp(cmp_operator, ">") != 0 && strcmp(cmp_operator, "<") != 0 && strcmp(cmp_operator, "=") != 0) {
        elog(WARNING, "Invalid comparison operator: %s. Only '>', '<', '=' are allowed.", cmp_operator);
        PG_RETURN_INT32(0);
    }

    // 检查 text_value 是否为 NULL 或空
    if (text_value == NULL || strlen(text_value) == 0) {
        elog(WARNING, "Column value cannot be NULL or empty");
        PG_RETURN_INT32(0);
    }

    // 检查表是否存在
    if (!OidIsValid(relid)) {
        elog(WARNING, "Table %s does not exist in schema %s", TABLESCORE, SCHEMANAME);
        PG_RETURN_INT32(0);
    }

    get_scan_key_params(col_type, cmp_operator, &strategy, &procedure);

    // 创建 ScanKey
    scan_argument = get_datum_by_col_type(col_type, text_value);

    // 创建 ScanKey
    ScanKeyInit(&scanKey,
                col_index,
                strategy,
                procedure,
                scan_argument);
    // 打开表
    rel = table_open(relid, RowExclusiveLock);
    if (!RelationIsValid(rel)) {
        elog(WARNING, "Failed to open table %s", TABLESCORE);
    }

    // 开始扫描
    scanDesc = systable_beginscan(rel, InvalidOid, false, NULL, 1, &scanKey);

    // 删除匹配的记录
    while ((tuple = systable_getnext(scanDesc)) != NULL) {
        rows++;
        simple_heap_delete(rel, &tuple->t_self);
    }

    // 结束扫描
    systable_endscan(scanDesc);
    table_close(rel, RowExclusiveLock);
    log_operateRows(TABLESCORE,deleteType,rows);
    PG_RETURN_INT32(rows);
}

PG_FUNCTION_INFO_V1(query_scores_nospi);

Datum query_scores_nospi(PG_FUNCTION_ARGS) {
    char *col_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
    char *cmp_operator = text_to_cstring(PG_GETARG_TEXT_PP(1));
    char *input_value = text_to_cstring(PG_GETARG_TEXT_PP(2));
#define PG_STAT_GET_SCORE_COLS 6
    Oid relid = -1;
    Relation rel = NULL;
    AttrNumber col_index = -1;
    int col_type = -1;
    ScanKeyData scanKey;
    StrategyNumber strategy = 0;
    RegProcedure procedure = 0;
    HeapTuple tuple = NULL;
    Datum values[PG_STAT_GET_SCORE_COLS] = {0};
    bool nulls[PG_STAT_GET_SCORE_COLS] = {false};
    Datum scan_argument;
    SysScanDesc scanDesc;

    ReturnSetInfo *rsinfo = (ReturnSetInfo *)fcinfo->resultinfo;
    InitMaterializedSRF(fcinfo, 0);

    // 检查 col_name 是否为 NULL 或空
    if (col_name == NULL || strlen(col_name) == 0) {
        elog(WARNING, "Column name cannot be NULL or empty");
        PG_RETURN_INT32(0);
    }

    // 检查 cmp_operator 是否为有效的运算符
    if (strcmp(cmp_operator, ">") != 0 && strcmp(cmp_operator, "<") != 0 && strcmp(cmp_operator, "=") != 0) {
        elog(WARNING, "Invalid comparison operator: %s. Only '>', '<', '=' are allowed.", cmp_operator);
        PG_RETURN_INT32(0);
    }

    // 检查 input_value 是否为 NULL 或空
    if (input_value == NULL || strlen(input_value) == 0) {
        elog(WARNING, "Input value cannot be NULL or empty");
        PG_RETURN_INT32(0);
    }


    relid = get_relname_relid(TABLESCORE, get_namespace_oid(SCHEMANAME, true));
    rel = table_open(relid, AccessShareLock);
    if (!RelationIsValid(rel)) {
        elog(WARNING, "Failed to open table %s",TABLESCORE);
        PG_RETURN_INT32(0);
    }

    if (!RelationIsValid(rel)) {
        ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("Failed to open table score")));
    }

    col_index = get_column_index(TABLESCORE,col_name);
    col_type = get_column_type(col_name);
    // Prepare for scanning

    get_scan_key_params(col_type, cmp_operator, &strategy, &procedure);

    // 获取需要扫描的列的值
    scan_argument = get_datum_by_col_type(col_type, input_value);

    ScanKeyInit(&scanKey, col_index, strategy, procedure, scan_argument);

    // Begin scanning the table
    scanDesc = systable_beginscan(rel, InvalidOid, false, NULL, 1, &scanKey);

    // Process results


    while ((tuple = systable_getnext(scanDesc)) != NULL) {
        heap_deform_tuple(tuple, RelationGetDescr(rel), values, nulls);
        tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
    }

    // Clean up
    systable_endscan(scanDesc);
    table_close(rel, AccessShareLock);

    return (Datum) 0;
}


PG_FUNCTION_INFO_V1(update_scores_nospi);

Datum update_scores_nospi(PG_FUNCTION_ARGS) {
    ArrayType *operations_array = PG_GETARG_ARRAYTYPE_P(0);
    Datum *operations = NULL;
    bool *nulls = NULL;
    int n_operations = 0;
    Relation rel = NULL;
    HeapTuple tuple = NULL;
    ScanKeyData scanKey;
    SysScanDesc scanDesc = NULL;
    int32 rows = 0; //被更新的行数

    Oid schemaId = get_namespace_oid(SCHEMANAME, false);
    Oid relid = get_relname_relid(TABLESCORE, schemaId);

    // 检查传入的数组是否为 NULL
    if (operations_array == NULL) {
        elog(WARNING, "Input operations array cannot be NULL");
        PG_RETURN_INT32(0);
    }

   if (!OidIsValid(relid)) {
        elog(WARNING, "Table %s does not exist in schema %s", TABLESCORE, SCHEMANAME);
        PG_RETURN_INT32(0);
    }

    // 解构数组，获取每个更新操作
    deconstruct_array(operations_array, TEXTOID, -1, false, 'i', &operations, &nulls, &n_operations);



    // Open table
    rel = table_open(relid, RowExclusiveLock);
    if (!RelationIsValid(rel)) {
        ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("Failed to open table score")));
    }

    // 遍历每个操作
    for (int i = 0; i < n_operations; i++) {
        if (!nulls[i]) {
            char *operation_record = text_to_cstring(DatumGetTextP(operations[i]));
            char col_name[256], cmp_operator[256], input_value[256];
            char update_col_name[256], new_value[256];
            AttrNumber col_index = 0;
            int col_type = 0;
            StrategyNumber strategy = 0;
            RegProcedure procedure = 0;
            Datum key_value = 0;
            AttrNumber update_col_index = 0;
            int update_col_type = 0;
            Datum update_value = 0;
            // 使用 sscanf 解析操作字符串
            if (sscanf(operation_record, " %255[^,],%255[^,],%255[^,],%255[^,],%255[^\n]",
                   col_name, cmp_operator, input_value, update_col_name, new_value) != 5) {
                elog(WARNING, "Invalid operation format at index %d", i);
                continue; // 跳过不正确的格式
            }

            // 校验操作中的列名和比较运算符
            if (strcmp(cmp_operator, ">") != 0 && strcmp(cmp_operator, "<") != 0 && strcmp(cmp_operator, "=") != 0) {
                elog(WARNING, "Invalid comparison operator: %s. Only '>', '<', '=' are allowed.", cmp_operator);
                continue; // 跳过不合法的运算符
            }
            //判断id列和ranking列的值是否存在于表中
            if (strcmp(update_col_name, "id") == 0)
            {
                if (check_value_exists(rel,"id",new_value,NULL,NULL,TABLESCORE))
                {
                    break;
                }
            }

            if (strcmp(update_col_name, "ranking") == 0)
            {
                if (check_value_exists(rel,"ranking",new_value,NULL,NULL,TABLESCORE))
                {
                    break;
                }
            }

            // 获取列索引和策略
            get_index_type_bycolname(TABLESCORE,col_name,&col_type,&col_index);

            get_scan_key_params(col_type, cmp_operator, &strategy, &procedure);

            // 获取需要扫描的列的值
            key_value = get_datum_by_col_type(col_type, input_value);

            // 初始化扫描键
            ScanKeyInit(&scanKey, col_index, strategy, procedure, key_value);

            // 开始扫描
            scanDesc = systable_beginscan(rel, InvalidOid, false, NULL, 1, &scanKey);

            // 获取更新列的索引

            get_index_type_bycolname(TABLESCORE,update_col_name,&update_col_type,&update_col_index);
            // 获取需要更新的列的值
            update_value = get_datum_by_col_type(update_col_type, new_value);

            // 更新匹配的记录
            while ((tuple = systable_getnext(scanDesc)) != NULL) {
                // 修改元组
                bool nulls[SCORE_FIELDS_COUNT] = {false};
                Datum values[SCORE_FIELDS_COUNT];
                HeapTuple new_tuple;
                heap_deform_tuple(tuple, RelationGetDescr(rel), values, nulls);

                // 更新指定列
                values[update_col_index - 1] = update_value;

                // 创建新的 HeapTuple
                new_tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);

                // 执行更新
                CatalogTupleUpdate(rel, &tuple->t_self, new_tuple);
                CommandCounterIncrement();
                // 释放新元组
                pfree(new_tuple);
                rows++;
            }

            systable_endscan(scanDesc);
        }
    }

    log_operateRows(TABLESCORE,updateType,rows);
    // 关闭表
    table_close(rel, RowExclusiveLock);

    PG_RETURN_INT32(rows);
}